When To Use SproutCore, and When Not To

written by mtaby

I could tell you to use SproutCore for every single web destination you build, but that would be disingenuous. SproutCore is built to address a certain class of applications that need the help of its robust binding and observer layer, as well as its ecosystem of packages (DataStore, gesture support, etc). There are other classes of applications that simply don’t have the same functionality.

On a spectrum of interactivity, you have Wikipedia on one end, and iCloud.com on the other. The high level of interactivity in a suite of applications like iCloud mandates a foundation that dictates architecture and allows the developers to think and develop at a higher level of abstraction. Wikipedia, on the other hand, lacks rich interactivity or client-side data management, so it does not need state management.

The key consideration you have to make is whether or not you need state management on the client (the browser). When I refer to “state” in this context, I’m referring to a couple of types of state:

  • Server data: If you need to fetch, store, and maintain a data set delivered to you from the server, then SproutCore’s bindings model and the DataStore package ensure that what the user sees and what your server knows about never get out of sync.

  • Application state: In an application’s UI, multiple related pieces of information are usually displayed simultaneously: the aggregate number of items in a list, a list of the most recent items in a collection, etc. Using traditional web development techniques to maintain this sort of application state necessitates a large amount of boilerplate code writing and upkeep.

Finally, SproutCore helps manage the complexity of the code base and keeps marginal-cost of new features low over the lifetime of your project.

[caption id=”attachment_1600” align=”aligncenter” width=”523” caption=”The effort required to scale your application in terms of complexity grows at a much lower rate than a traditional, roll-your-own technique.”][/caption]

This is emergent from SproutCore’s binding and observer layer. Because you describe the path your data follows from the model layer to the view layer, any new feature that impacts the data in the model layer will automatically propagate through your application.

Alternatively, when using an event-driven system where you primarily respond to user action, adding a new feature requires you to review the list of current features in your application to ensure that the new addition integrates well with the others. This normally involves keeping disparate functionality in sync by hand, which can become unruly over time.

You, the application developer, write and maintain the application code. However, the community writes and maintains the framework code, allowing the benefits trickle down to you when you update the framework. As a result, by offloading the code you have to write from the application to the framework, you ensure that you build robust, interactive, and fast applications.

Video from Our International Meetups!

written by mdouglas

For those of you who haven’t heard, SproutCore meetups are going international! In the past three weeks, we’ve had meetups in Malaysia, New Zealand, Canada, and Germany–and more are in the works!

As often as possible, we make a remote session for these talks available; for those of you who couldn’t make it, we have recordings of two of the talks available now.

Here, Greg Moeck talks to the Kuala Lumpur meetup group about SproutCore Views Best Practices:

Tom Dale also gave a talk to the Vancouver group about SproutCore UI. The sound is slightly less clear on this recording– our apologies!

The Q&As; from these sessions are also available on the SproutCore Vimeo channel.

We always love hearing from the Core Team, but the community is also interested in demos and real-world applications using SproutCore. If you’d like to speak at a meetup, or just show some code and workshop it with other SproutCore devs, let us know!

We have lots to learn from each other, and want to hear from YOU!

Dispatch From The Edge: Handlebars Debugging Helpers

written by gmoeck

Greetings all! It’s been a bit since our last Dispatch, and the edge waits for nobody, so quite a lot has been going on. This week I’m going to focus on a couple of helpers that have been added to aid in the debugging of view rendering with Handlebars templates.

Generally, when people start trying to debug a problem in their JavaScript, they go about it in one of two ways. Either they try and use console.log to log a value to the console, or they insert a debugger statement that essentially functions as a breakpoint that stops execution of the application at that point.

Both of these techniques have always worked fine inside of SproutCore, except inside of a Handlebars template. Since Handlebars does not allow you to execute arbitrary JavaScript code within your template, you were left trying to log or debug the view property that you were trying to display.

Enter two new Handlebars helpers: log and debugger. Now you can log a property on the template or add a debugger statement to pause execution directly into the template itself. What would this look like? Consider the following code:

  <div class="some-item">
    {{log name}}
    {{debugger}}
    {{name}}
  </div>

This would log the name property to the console, and then stop the execution, setting the current “this” property to the template. This means that you can do a this.get(‘name’), or any other property, on the template and it will print the value to the console window.

If you interested in how to do this sort of thing for yourself, the source file is very simple and you can read it for yourself here.

Hope this helps you in debugging your busted templates. Until next time this is Greg signing off.

Structuring Your SproutCore Application: Part 2

written by ccampbell

In Part 1, we started developing a Contact application for managing groups and people. Continuing with Part 2, we’re going to be implementing the application’s functionality in a way that’s going to scale with its complexity.

Loading The Application’s Data

Now that we have the base of our application, we need to start by loading some data into the application. Before we proceed, let’s make sure we’re on the same page. Check out step 4:

	git checkout step4

We’re going to do this using SC.FixturesDataSource. It allows you to use fixture data local to your application, but also simulates remote responses. That means when you implement a connection to a remote server, your application works as expected. Let’s create the FixturesDataSource in apps/contact/data_sources/fixtures.js:

	Contact.FixturesDataSource = SC.FixturesDataSource.extend({
	  simulateRemoteResponse: YES,
	  latency: 250
	});

We’ve defined a new FixturesDataSource class, turned on the simulation of remote responses, and set the latency to 250ms. This is a typical amount of latency between a client and a server but you can tweak this number to suit your needs. The last thing we need to do is make our application’s store object load its data using the new data source. This is done by going into apps/contact/contact.js and changing the store’s definition to the following:

	Contact = SC.Application.create({
	  store: SC.Store.create().from('Contact.FixturesDataSource')
	});

Now we have the data loading asynchronously from the server. Let’s implement some logic to load the data into the application. We’ll do this in a state that occurs when the application is ready to be used, so we can potentially display a loading indicator, but this pattern should also be usable if you want to go straight into the application and load the data using a different UI. Inside our ReadyState in apps/contact/states/ready.js, we’re going to add a loading substate and declare it as our initialSubstate.

	Contact.ReadyState = SC.State.extend({
	  initialSubstate: 'loading'
	});

This is going to make sure that when the application becomes ready (ie. enters the ready state), we go into the loading substate and start loading data. To load the data, we’re going to use the DataStore API. We first create a query for a record type – you can add filtering at this level so the query only manages certain records.

	var query = SC.Query.local(Contact.Group);

This query will fetch all of the Contact.Group records. We execute this query and load data from the server by passing it to the application’s store:

	var data = Contact.store.find(query);

This is going to execute the fetch function on our FixturesDataSource (the default one loads the records into the store, so we don’t need to implement this ourselves). The data object is going to be a SC.RecordArray, which wraps an array of records and provides some nice abstractions. We need to observe the status property on the data object so we know when its records have been loaded into the store.

Once that is done, we’re going to want to set the content of an SC.ArrayController, and let the parent state know that we finished loading the data for the record type (because we’re going to load multiple things and they may not load in order):

	data.addObserver('status', this, function observer() {
 	 if (data.get('status') === SC.Record.READY_CLEAN) {
	    data.removeObserver('status', this, observer);
	    Contact.groupsController.set('content', data);
	    this.get('statechart').invokeStateMethod('dataLoaded');
	  }
	});

Once this is done, we change the statechart to the none state. This state represents the application when the data has been loaded and we’re waiting for user interaction.

Adding Application State

Let’s move on to step 5:

	git checkout step5

Now we need to implement the UI so that we can allow the user to interact with the application. The statechart is going to receive actions, and, based on those actions, will coordinate changes to the application.

Our application has two substates once we’re ready to receive user actions: we want to show a screen for editing groups, and we screen for editing people. Thus, we add two substates to the none state. In these substates, we want to change the Contact.displayController so the UI will update and display the correct view:

    group: SC.State.design({
      enterState: function() {
        Contact.displayController.set('nowShowing', 'Contact.groupView');
      }
    }),

    person: SC.State.design({
      enterState: function() {
        Contact.displayController.set('nowShowing', 'Contact.personView');
      }
    })

However, we still need to allow the statechart to enter and exit these states. You do this by adding actions – functions on the statechart that call gotoState. We need one for both the group and person, and they should be added to the none state because the user can switch to a user or person from any of its substates.

    showGroup: function() {
      this.gotoState('ready.none.group');
      return YES;
    },

    showPerson: function() {
      this.gotoState('ready.none.person');
      return YES;
    }

We’ve now implemented our statechart logic.

Styling

In step 6, we’re going to implement some styling changes, and some other small fixes. I’ll allow you to review the code on the repository.

Nested Stores and Saving Data

For applications that have editing functionality, some of the time you’ll want to wait until the user saves changes to persist that to the server. You’ll also want an easy way of discarding the changes made by the user if they cancel their changes. SproutCore’s DataStore framework provides the concept of nested store to allow you to bake this functionality into your app quickly and easily. Let’s check out step 7:

    git checkout step7

Whenever we enter the group or person state (i.e., start editing records of those types), we’ll want to create a nested store to buffer the changes.

	store = Contact.store.chain();

We need to make sure that the record the user is editing is from the nested store, not the parent store. To do this, we simply use store.find(Contact.Group, guid); to get the record from the nested store. We now need to set up the actions that will get called from our “Save” and “Cancel” buttons:

	cancel: function() {
	  this._store.discardChanges();
	},

	save: function() {
	  this._store.commitRecords(true);
	}

As you can see, we called store.discardChanges() to get rid of changes and store.commitRecords() to save them to the parent store. We also need to make sure we get rid of any changes and destroy the nested store when we exit the current state:

	exitState: function() {
	  this._store.discardChanges().destroy();
	}

Adding a Sign In Page

Now that we have the in-application features implemented, let’s add a login page. Check out step 8:

	git checkout step8

Let’s review the changes we’ve made in this step. First, we want to make sure that the app enters the signIn state when it starts, not the ready state. We also need to the add a new pane to the application, because we’re displaying a different view structure from the one we had for the main application.

We give it a TemplateView for a child view, and then provide a template which will render an input field for the email and password. We also render out a button which will call an action on our statechart when we want to proceed. We also create a controller which holds the value of the email and password.

We now have two states. One is when the form is being displayed, and fields are enabled. Once we dealt with the cases where the email or password is empty, we determine that a request is valid and switch into the second state. This is the request state, where we would send the user’s information to the server and it would return successfully or return an error. For our purposes, I’ve just added a timeout of 250ms and then we proceed to the application’s ready state.

Conclusion

We’ve just roughed out a small application in SproutCore. There are some rough edges, especially with the styling, but it has some advanced functionality, and it shows how using things that come with SproutCore out of the box can help you quickly iterate and scale your application. For example, adding the sign in page required few changes to the existing code; writing this application also demonstrated how using bindings and statecharts help you easily manage your application flow and logic.

As most of us know, writing complex applications with any framework can be difficult. I hope these blog posts help you write applications in a way that allows your application structure to scale.